package com.d3works.apps.account.service.move;

import com.d3works.apps.account.db.Account;
import com.d3works.apps.account.db.AnalyticAccount;
import com.d3works.apps.account.db.AnalyticJournal;
import com.d3works.apps.account.db.AnalyticMoveLine;
import com.d3works.apps.account.db.Journal;
import com.d3works.apps.account.db.Move;
import com.d3works.apps.account.db.MoveLine;
import com.d3works.apps.account.db.repo.AccountRepository;
import com.d3works.apps.account.db.repo.AccountTypeRepository;
import com.d3works.apps.account.db.repo.AnalyticAccountRepository;
import com.d3works.apps.account.db.repo.AnalyticJournalRepository;
import com.d3works.apps.account.db.repo.JournalRepository;
import com.d3works.apps.account.db.repo.JournalTypeRepository;
import com.d3works.apps.account.db.repo.MoveLineRepository;
import com.d3works.apps.account.db.repo.MoveRepository;
import com.d3works.apps.account.exception.AccountExceptionMessage;
import com.d3works.apps.account.service.PeriodServiceAccount;
import com.d3works.apps.account.service.app.AppAccountService;
import com.d3works.apps.account.service.config.AccountConfigService;
import com.d3works.apps.account.service.fixedasset.FixedAssetGenerationService;
import com.d3works.apps.account.service.moveline.MoveLineTaxService;
import com.d3works.apps.account.service.moveline.MoveLineToolService;
import com.d3works.apps.base.db.Company;
import com.d3works.apps.base.db.Partner;
import com.d3works.apps.base.db.repo.PartnerRepository;
import com.d3works.apps.base.db.repo.PeriodRepository;
import com.d3works.apps.base.service.app.AppBaseService;
import com.d3works.auth.AuthUtils;
import com.d3works.auth.db.User;
import com.d3works.common.ObjectUtils;
import com.d3works.db.JPA;
import com.d3works.db.Query;
import com.d3works.exception.D3worksException;
import com.d3works.exception.db.repo.TraceBackRepository;
import com.d3works.exception.service.TraceBackService;
import com.d3works.i18n.I18n;
import com.d3works.meta.MetaStore;
import com.d3works.meta.schema.views.Selection.Option;
import com.google.common.base.Splitter;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import com.google.inject.servlet.RequestScoped;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@RequestScoped
public class MoveValidateServiceImpl implements MoveValidateService {
  private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

  protected MoveLineControlService moveLineControlService;
  protected MoveLineToolService moveLineToolService;
  protected AccountConfigService accountConfigService;
  protected MoveSequenceService moveSequenceService;
  protected MoveCustAccountService moveCustAccountService;
  protected MoveToolService moveToolService;
  protected MoveInvoiceTermService moveInvoiceTermService;
  protected MoveRepository moveRepository;
  protected AccountRepository accountRepository;
  protected PartnerRepository partnerRepository;
  protected AppBaseService appBaseService;
  protected AppAccountService appAccountService;
  protected FixedAssetGenerationService fixedAssetGenerationService;
  protected MoveLineTaxService moveLineTaxService;
  protected PeriodServiceAccount periodServiceAccount;
  protected MoveControlService moveControlService;

  @Inject
  public MoveValidateServiceImpl(
      MoveLineControlService moveLineControlService,
      MoveLineToolService moveLineToolService,
      AccountConfigService accountConfigService,
      MoveSequenceService moveSequenceService,
      MoveCustAccountService moveCustAccountService,
      MoveToolService moveToolService,
      MoveInvoiceTermService moveInvoiceTermService,
      MoveRepository moveRepository,
      AccountRepository accountRepository,
      PartnerRepository partnerRepository,
      AppBaseService appBaseService,
      AppAccountService appAccountService,
      FixedAssetGenerationService fixedAssetGenerationService,
      MoveLineTaxService moveLineTaxService,
      PeriodServiceAccount periodServiceAccount,
      MoveControlService moveControlService) {

    this.moveLineControlService = moveLineControlService;
    this.moveLineToolService = moveLineToolService;
    this.accountConfigService = accountConfigService;
    this.moveSequenceService = moveSequenceService;
    this.moveCustAccountService = moveCustAccountService;
    this.moveToolService = moveToolService;
    this.moveInvoiceTermService = moveInvoiceTermService;
    this.moveRepository = moveRepository;
    this.accountRepository = accountRepository;
    this.partnerRepository = partnerRepository;
    this.appBaseService = appBaseService;
    this.appAccountService = appAccountService;
    this.fixedAssetGenerationService = fixedAssetGenerationService;
    this.moveLineTaxService = moveLineTaxService;
    this.periodServiceAccount = periodServiceAccount;
    this.moveControlService = moveControlService;
  }

  /**
   * In move lines, fill the dates field and the partner if they are missing, and fill the counter.
   *
   * @param move
   */
  @Override
  public void completeMoveLines(Move move) {
    LocalDate date = move.getDate();
    Partner partner = move.getPartner();

    int counter = 1;
    for (MoveLine moveLine : move.getMoveLineList()) {
      if (moveLine.getDate() == null) {
        moveLine.setDate(date);
      }

      if (moveLine.getAccount() != null
          && moveLine.getAccount().getUseForPartnerBalance()
          && moveLine.getDueDate() == null) {
        moveLine.setDueDate(date);
      }

      if (moveLine.getOriginDate() == null) {
        if (ObjectUtils.notEmpty(move.getOriginDate())) {
          moveLine.setOriginDate(move.getOriginDate());
        } else {
          moveLine.setOriginDate(date);
        }
      }
      moveLine.setCounter(counter);
      counter++;
    }
  }

  @Override
  public void checkPreconditions(Move move) throws D3worksException {

    Journal journal = move.getJournal();
    Company company = move.getCompany();

    if (company == null) {
      throw new D3worksException(
          TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
          I18n.get(AccountExceptionMessage.MOVE_3),
          move.getReference());
    }

    if (journal == null) {
      throw new D3worksException(
          TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
          I18n.get(AccountExceptionMessage.MOVE_2),
          move.getReference());
    }

    if (move.getPeriod() == null) {
      throw new D3worksException(
          TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
          I18n.get(AccountExceptionMessage.MOVE_4),
          move.getReference());
    }
    if (!CollectionUtils.isEmpty(move.getPeriod().getClosedJournalSet())
        && move.getPeriod().getClosedJournalSet().contains(move.getJournal())) {
      throw new D3worksException(
          TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
          String.format(
              I18n.get(AccountExceptionMessage.MOVE_13),
              move.getJournal().getCode(),
              move.getPeriod().getCode()));
    }

    if (move.getMoveLineList() == null || move.getMoveLineList().isEmpty()) {
      throw new D3worksException(
          TraceBackRepository.CATEGORY_INCONSISTENCY,
          I18n.get(AccountExceptionMessage.MOVE_8),
          move.getReference());
    }

    if (move.getCurrency() == null) {
      throw new D3worksException(
          TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
          I18n.get(AccountExceptionMessage.MOVE_12),
          move.getReference());
    }

    if (appAccountService.getAppAccount().getManageCutOffPeriod()
        && move.getFunctionalOriginSelect() != MoveRepository.FUNCTIONAL_ORIGIN_CUT_OFF
        && !moveToolService.checkMoveLinesCutOffDates(move)) {
      throw new D3worksException(
          TraceBackRepository.CATEGORY_MISSING_FIELD,
          I18n.get(AccountExceptionMessage.MOVE_MISSING_CUT_OFF_DATE));
    }

    moveControlService.checkSameCompany(move);

    if (move.getMoveLineList().stream()
        .allMatch(
            moveLine ->
                moveLine.getDebit().add(moveLine.getCredit()).compareTo(BigDecimal.ZERO) == 0)) {
      throw new D3worksException(
          TraceBackRepository.CATEGORY_INCONSISTENCY,
          I18n.get(AccountExceptionMessage.MOVE_8),
          move.getReference());
    }

    checkClosurePeriod(move);
    checkInactiveAnalyticJournal(move);
    checkInactiveAccount(move);
    checkInactiveAnalyticAccount(move);
    checkInactiveJournal(move);
    checkFunctionalOriginSelect(move);

    validateVatSystem(move);

    Integer functionalOriginSelect = move.getFunctionalOriginSelect();
    if (functionalOriginSelect != MoveRepository.FUNCTIONAL_ORIGIN_CLOSURE
        && functionalOriginSelect != MoveRepository.FUNCTIONAL_ORIGIN_OPENING) {
      for (MoveLine moveLine : move.getMoveLineList()) {
        Account account = moveLine.getAccount();
        if (account.getIsTaxAuthorizedOnMoveLine()
            && account.getIsTaxRequiredOnMoveLine()
            && moveLine.getTaxLine() == null) {
          throw new D3worksException(
              TraceBackRepository.CATEGORY_MISSING_FIELD,
              I18n.get(AccountExceptionMessage.MOVE_9),
              account.getCode(),
              account.getName(),
              moveLine.getName());
        }

        if (moveLine.getAnalyticDistributionTemplate() == null
            && ObjectUtils.isEmpty(moveLine.getAnalyticMoveLineList())
            && account.getAnalyticDistributionAuthorized()
            && account.getAnalyticDistributionRequiredOnMoveLines()) {
          throw new D3worksException(
              TraceBackRepository.CATEGORY_MISSING_FIELD,
              I18n.get(AccountExceptionMessage.MOVE_10),
              account.getName(),
              moveLine.getName());
        }

        if (account != null
            && !account.getAnalyticDistributionAuthorized()
            && (moveLine.getAnalyticDistributionTemplate() != null
                || (moveLine.getAnalyticMoveLineList() != null
                    && !moveLine.getAnalyticMoveLineList().isEmpty()))) {
          throw new D3worksException(
              move,
              TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
              I18n.get(AccountExceptionMessage.MOVE_11),
              moveLine.getName());
        }

        moveLineControlService.validateMoveLine(moveLine);
        moveLineControlService.checkAccountCompany(moveLine);
        moveLineControlService.checkJournalCompany(moveLine);

        moveLineToolService.checkDateInPeriod(move, moveLine);
      }

      moveLineTaxService.checkTaxMoveLines(move);

      this.validateWellBalancedMove(move);

      if (move.getJournal() != null
          && move.getPartner() != null
          && move.getJournal().getHasDuplicateDetectionOnOrigin()) {
        List<Move> moveList = moveToolService.getMovesWithDuplicatedOrigin(move);
        if (ObjectUtils.notEmpty(moveList)) {
          throw new D3worksException(
              move,
              TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
              I18n.get(AccountExceptionMessage.MOVE_DUPLICATE_ORIGIN_BLOCKING_MESSAGE),
              moveList.stream().map(Move::getReference).collect(Collectors.joining(",")),
              move.getPartner().getFullName(),
              move.getPeriod().getYear().getName());
        }
      }
    }
  }

  protected void checkFunctionalOriginSelect(Move move) throws D3worksException {
    Integer functionalOriginSelect = move.getFunctionalOriginSelect();
    if (functionalOriginSelect == null || functionalOriginSelect == 0) {
      return;
    }
    Journal journal = move.getJournal();
    String authorizedFunctionalOriginSelect = journal.getAuthorizedFunctionalOriginSelect();
    if (authorizedFunctionalOriginSelect != null
        && !(Splitter.on(",")
            .trimResults()
            .splitToList(authorizedFunctionalOriginSelect)
            .contains(functionalOriginSelect.toString()))) {

      Option selectionItem =
          MetaStore.getSelectionItem(
              "iaccount.move.functional.origin.select", functionalOriginSelect.toString());
      throw new D3worksException(
          TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
          I18n.get(AccountExceptionMessage.MOVE_14),
          selectionItem.getLocalizedTitle(),
          move.getReference(),
          journal.getName(),
          journal.getCode());
    }
  }

  protected void checkClosurePeriod(Move move) throws D3worksException {

    if (!periodServiceAccount.isAuthorizedToAccountOnPeriod(
        move.getPeriod(), AuthUtils.getUser())) {
      throw new D3worksException(
          TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
          I18n.get(AccountExceptionMessage.MOVE_PERIOD_IS_CLOSED));
    }
  }

  /**
   * Comptabiliser une écriture comptable.
   *
   * @param move
   * @throws D3worksException
   */
  @Override
  public void accounting(Move move) throws D3worksException {

    this.accounting(move, true);
  }

  /**
   * Comptabiliser une écriture comptable.
   *
   * @param move
   * @throws D3worksException
   */
  @Transactional(rollbackOn = {Exception.class})
  @Override
  public void accounting(Move move, boolean updateCustomerAccount) throws D3worksException {

    log.debug("Accounting of the move {}", move.getReference());

    this.checkPreconditions(move);

    log.debug("Precondition check of move {} OK", move.getReference());
    boolean dayBookMode =
        accountConfigService.getAccountConfig(move.getCompany()).getAccountingDaybook()
            && move.getJournal().getAllowAccountingDaybook();

    if (move.getPeriod().getStatusSelect() == PeriodRepository.STATUS_CLOSED
        && !move.getAutoYearClosureMove()) {
      if (dayBookMode
          && (move.getStatusSelect() == MoveRepository.STATUS_NEW
              || move.getStatusSelect() == MoveRepository.STATUS_SIMULATED)) {
        throw new D3worksException(
            TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
            I18n.get(AccountExceptionMessage.MOVE_DAYBOOK_FISCAL_PERIOD_CLOSED));
      } else {
        throw new D3worksException(
            TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
            I18n.get(AccountExceptionMessage.MOVE_ACCOUNTING_FISCAL_PERIOD_CLOSED));
      }
    }

    if (!dayBookMode || move.getStatusSelect() == MoveRepository.STATUS_DAYBOOK) {
      moveSequenceService.setSequence(move);
    }

    if (move.getPeriod().getStatusSelect() == PeriodRepository.STATUS_ADJUSTING) {
      move.setAdjustingMove(true);
    }

    moveInvoiceTermService.generateInvoiceTerms(move);
    moveInvoiceTermService.updateMoveLineDueDates(move);

    this.completeMoveLines(move);
    this.freezeAccountAndPartnerFieldsOnMoveLines(move);
    this.updateValidateStatus(move, dayBookMode);

    if (move.getStatusSelect() == MoveRepository.STATUS_ACCOUNTED) {
      this.generateFixedAssetMoveLine(move);
    }

    moveRepository.save(move);

    if (updateCustomerAccount) {
      moveCustAccountService.updateCustomerAccount(move);
    }
  }

  /**
   * This method may generate fixed asset for each moveLine of move. It will generate if
   * moveLine.fixedAssetCategory != null AND moveLine.account.accountType.technicalTypeSelect =
   * 'immobilisation'
   *
   * @param move
   * @throws D3worksException
   * @throws NullPointerException if move is null or if a line does not have an account
   */
  @Override
  public void generateFixedAssetMoveLine(Move move) throws D3worksException {
    log.debug("Starting generation of fixed assets for move " + move);
    Objects.requireNonNull(move);

    List<MoveLine> moveLineList = move.getMoveLineList();
    if (moveLineList != null) {
      for (MoveLine line : moveLineList) {
        if (line.getFixedAssetCategory() != null
            && line.getAccount()
                .getAccountType()
                .getTechnicalTypeSelect()
                .equals(AccountTypeRepository.TYPE_IMMOBILISATION)) {
          fixedAssetGenerationService.generateAndSaveFixedAsset(move, line);
        }
      }
    }
  }

  /**
   * Procédure permettant de vérifier qu'une écriture est équilibré, et la validé si c'est le cas
   *
   * @param move Une écriture
   * @throws D3worksException
   */
  @Override
  public void validateWellBalancedMove(Move move) throws D3worksException {

    log.debug("Well-balanced validation on account move {}", move.getReference());

    if (move.getMoveLineList() != null) {

      BigDecimal totalDebit = BigDecimal.ZERO;
      BigDecimal totalCredit = BigDecimal.ZERO;

      for (MoveLine moveLine : move.getMoveLineList()) {

        if (moveLine.getDebit().compareTo(BigDecimal.ZERO) > 0
            && moveLine.getCredit().compareTo(BigDecimal.ZERO) > 0) {
          throw new D3worksException(
              move,
              TraceBackRepository.CATEGORY_INCONSISTENCY,
              I18n.get(AccountExceptionMessage.MOVE_6),
              moveLine.getName());
        }

        totalDebit = totalDebit.add(moveLine.getDebit());
        totalCredit = totalCredit.add(moveLine.getCredit());
      }

      if (totalDebit.compareTo(totalCredit) != 0) {
        throw new D3worksException(
            move,
            TraceBackRepository.CATEGORY_INCONSISTENCY,
            I18n.get(AccountExceptionMessage.MOVE_7),
            move.getReference(),
            totalDebit,
            totalCredit);
      }
    }
  }

  @Override
  public void updateValidateStatus(Move move, boolean daybook) throws D3worksException {
    if (move.getStatusSelect() == MoveRepository.STATUS_DAYBOOK
        || !daybook
        || (daybook
            && (move.getStatusSelect() == MoveRepository.STATUS_NEW
                || move.getStatusSelect() == MoveRepository.STATUS_SIMULATED)
            && (move.getTechnicalOriginSelect() == MoveRepository.TECHNICAL_ORIGIN_AUTOMATIC)
            && (move.getFunctionalOriginSelect() == MoveRepository.FUNCTIONAL_ORIGIN_OPENING
                || move.getFunctionalOriginSelect() == MoveRepository.FUNCTIONAL_ORIGIN_CLOSURE))) {
      move.setStatusSelect(MoveRepository.STATUS_ACCOUNTED);
      move.setAccountingDate(appBaseService.getTodayDate(move.getCompany()));
    } else {
      move.setStatusSelect(MoveRepository.STATUS_DAYBOOK);
    }
  }

  @Override
  @Transactional(rollbackOn = {Exception.class})
  public void updateInDayBookMode(Move move) throws D3worksException {

    this.checkPreconditions(move);

    Set<Partner> partnerSet = new HashSet<>();

    partnerSet.addAll(this.getPartnerOfMoveBeforeUpdate(move));
    partnerSet.addAll(moveCustAccountService.getPartnerOfMove(move));

    List<Partner> partnerList = new ArrayList<>();
    partnerList.addAll(partnerSet);

    this.freezeAccountAndPartnerFieldsOnMoveLines(move);
    moveRepository.save(move);

    moveCustAccountService.updateCustomerAccount(partnerList, move.getCompany());
  }

  /**
   * Get the distinct partners of an account move that impact the partner balances
   *
   * @param move
   * @return A list of partner
   */
  @Override
  public List<Partner> getPartnerOfMoveBeforeUpdate(Move move) {
    List<Partner> partnerList = new ArrayList<Partner>();
    for (MoveLine moveLine : move.getMoveLineList()) {
      if (moveLine.getAccountId() != null) {
        Account account = accountRepository.find(moveLine.getAccountId());
        if (account != null
            && account.getUseForPartnerBalance()
            && moveLine.getPartnerId() != null) {
          Partner partner = partnerRepository.find(moveLine.getPartnerId());
          if (partner != null && !partnerList.contains(partner)) {
            partnerList.add(partner);
          }
        }
      }
    }
    return partnerList;
  }

  /**
   * Method that freeze the account and partner fields on move lines
   *
   * @param move
   */
  @Override
  public void freezeAccountAndPartnerFieldsOnMoveLines(Move move) {
    for (MoveLine moveLine : move.getMoveLineList()) {

      Account account = moveLine.getAccount();

      moveLine.setAccountId(account.getId());
      moveLine.setAccountCode(account.getCode());
      moveLine.setAccountName(account.getName());

      Partner partner = moveLine.getPartner();

      if (partner != null) {
        moveLine.setPartnerId(partner.getId());
        moveLine.setPartnerFullName(partner.getSimpleFullName());
        moveLine.setPartnerSeq(partner.getPartnerSeq());
      }
      if (moveLine.getTaxLine() != null) {
        moveLine.setTaxRate(moveLine.getTaxLine().getValue());
        moveLine.setTaxCode(moveLine.getTaxLine().getTax().getCode());
      }
    }
  }

  @Override
  public String accountingMultiple(List<Integer> moveIds) {
    String errors = "";
    if (moveIds == null) {
      return errors;
    }
    User user = AuthUtils.getUser();
    int i = 0;
    for (Integer moveId : moveIds) {
      Move move = moveRepository.find(moveId.longValue());
      try {
        if (!periodServiceAccount.isAuthorizedToAccountOnPeriod(move.getPeriod(), user)) {
          throw new D3worksException(
              TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
              String.format(
                  I18n.get(AccountExceptionMessage.ACCOUNT_PERIOD_TEMPORARILY_CLOSED),
                  move.getReference()));
        }
        if (move.getStatusSelect() != MoveRepository.STATUS_ACCOUNTED
            && move.getStatusSelect() != MoveRepository.STATUS_CANCELED) {
          accounting(move);
        }
      } catch (Exception e) {
        TraceBackService.trace(e);
        if (errors.length() > 0) {
          errors = errors.concat(", ");
        }
        errors = errors.concat(move.getReference());
      } finally {
        if (++i % 20 == 0) {
          JPA.clear();
        }
      }
    }

    return errors;
  }

  public void accountingMultiple(Query<Move> moveListQuery) throws D3worksException {
    Move move;

    while (!((move = moveListQuery.fetchOne()) == null)) {
      accounting(move);
      JPA.clear();
    }
  }

  protected void checkInactiveAnalyticAccount(Move move) throws D3worksException {
    if (move != null && CollectionUtils.isNotEmpty(move.getMoveLineList())) {
      List<String> inactiveList =
          move.getMoveLineList().stream()
              .map(MoveLine::getAnalyticMoveLineList)
              .filter(Objects::nonNull)
              .flatMap(Collection::stream)
              .map(AnalyticMoveLine::getAnalyticAccount)
              .filter(
                  analyticAccount ->
                      analyticAccount.getStatusSelect() != null
                          && analyticAccount.getStatusSelect()
                              != AnalyticAccountRepository.STATUS_ACTIVE)
              .distinct()
              .map(AnalyticAccount::getCode)
              .collect(Collectors.toList());
      if (inactiveList.size() == 1) {
        throw new D3worksException(
            TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
            I18n.get(AccountExceptionMessage.INACTIVE_ANALYTIC_ACCOUNT_FOUND),
            inactiveList.get(0));
      } else if (inactiveList.size() > 1) {
        throw new D3worksException(
            TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
            I18n.get(AccountExceptionMessage.INACTIVE_ANALYTIC_ACCOUNTS_FOUND),
            inactiveList.stream().collect(Collectors.joining(", ")));
      }
    }
  }

  protected void checkInactiveAnalyticJournal(Move move) throws D3worksException {
    if (move != null && CollectionUtils.isNotEmpty(move.getMoveLineList())) {
      List<String> inactiveList =
          move.getMoveLineList().stream()
              .map(MoveLine::getAnalyticMoveLineList)
              .filter(Objects::nonNull)
              .flatMap(Collection::stream)
              .map(AnalyticMoveLine::getAnalyticJournal)
              .filter(
                  analyticJournal ->
                      analyticJournal.getStatusSelect() != null
                          && analyticJournal.getStatusSelect()
                              != AnalyticJournalRepository.STATUS_ACTIVE)
              .distinct()
              .map(AnalyticJournal::getName)
              .collect(Collectors.toList());
      if (inactiveList.size() == 1) {
        throw new D3worksException(
            TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
            I18n.get(AccountExceptionMessage.INACTIVE_ANALYTIC_JOURNAL_FOUND),
            inactiveList.get(0));
      } else if (inactiveList.size() > 1) {
        throw new D3worksException(
            TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
            I18n.get(AccountExceptionMessage.INACTIVE_ANALYTIC_JOURNALS_FOUND),
            inactiveList.stream().collect(Collectors.joining(", ")));
      }
    }
  }

  protected void checkInactiveAccount(Move move) throws D3worksException {
    if (move != null && CollectionUtils.isNotEmpty(move.getMoveLineList())) {
      List<String> inactiveList =
          move.getMoveLineList().stream()
              .map(MoveLine::getAccount)
              .filter(Objects::nonNull)
              .filter(
                  account ->
                      account.getStatusSelect() != null
                          && account.getStatusSelect() != AccountRepository.STATUS_ACTIVE)
              .distinct()
              .map(Account::getCode)
              .collect(Collectors.toList());
      if (inactiveList.size() == 1) {
        throw new D3worksException(
            TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
            I18n.get(AccountExceptionMessage.INACTIVE_ACCOUNT_FOUND),
            inactiveList.get(0));
      } else if (inactiveList.size() > 1) {
        throw new D3worksException(
            TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
            I18n.get(AccountExceptionMessage.INACTIVE_ACCOUNTS_FOUND),
            inactiveList.stream().collect(Collectors.joining(", ")));
      }
    }
  }

  protected void checkInactiveJournal(Move move) throws D3worksException {
    if (move.getJournal() != null
        && move.getJournal().getStatusSelect() != JournalRepository.STATUS_ACTIVE) {
      throw new D3worksException(
          TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
          I18n.get(AccountExceptionMessage.INACTIVE_JOURNAL_FOUND),
          move.getJournal().getName());
    }
  }

  protected void validateVatSystem(Move move) throws D3worksException {
    if (!CollectionUtils.isEmpty(move.getMoveLineList())) {
      if ((move.getJournal().getJournalType() != null
              && (move.getJournal().getJournalType().getTechnicalTypeSelect()
                      == JournalTypeRepository.TECHNICAL_TYPE_SELECT_EXPENSE
                  || move.getJournal().getJournalType().getTechnicalTypeSelect()
                      == JournalTypeRepository.TECHNICAL_TYPE_SELECT_SALE))
          && isConfiguredVatSystem(move)
          && isConfigurationIssueOnVatSystem(move)) {
        throw new D3worksException(
            TraceBackRepository.CATEGORY_CONFIGURATION_ERROR,
            I18n.get(AccountExceptionMessage.TAX_MOVELINE_VAT_SYSTEM_DEFAULT),
            move.getReference());
      }
    }
  }

  protected boolean isConfiguredVatSystem(Move move) {
    for (MoveLine moveline : move.getMoveLineList()) {
      if (moveline.getTaxLine() != null
          && moveline.getAccount() != null
          && moveline.getAccount().getAccountType() != null
          && !AccountTypeRepository.TYPE_TAX.equals(
              moveline.getAccount().getAccountType().getTechnicalTypeSelect())
          && moveline.getAccount().getIsTaxAuthorizedOnMoveLine()
          && moveline.getAccount().getVatSystemSelect() != null
          && moveline.getAccount().getVatSystemSelect() != AccountRepository.VAT_SYSTEM_DEFAULT) {
        return true;
      }
    }
    return false;
  }

  protected boolean isConfigurationIssueOnVatSystem(Move move) {
    for (MoveLine moveline : move.getMoveLineList()) {
      if (moveline.getAccount() != null
          && moveline.getAccount().getAccountType() != null
          && AccountTypeRepository.TYPE_TAX.equals(
              moveline.getAccount().getAccountType().getTechnicalTypeSelect())
          && moveline.getVatSystemSelect() == MoveLineRepository.VAT_SYSTEM_DEFAULT) {
        return true;
      }
    }
    return false;
  }

  public void checkMoveLinesPartner(Move move) throws D3worksException {
    if (CollectionUtils.isEmpty(move.getMoveLineList())) {
      return;
    }
    for (MoveLine moveLine : move.getMoveLineList()) {
      moveLineControlService.checkPartner(moveLine);
    }
  }
}
